/**********************************************************************

  Audacity: A Digital Audio Editor

  FFmpegAPIResolver.h

  Dmitry Vedenko

**********************************************************************/
#pragma once

#include <memory>
#include <vector>
#include <map>

#include "../FFmpegTypes.h"
#include "../AVCodecID.h"

struct FFmpegFunctions;
class FFmpegLog;

class AVCodecContextWrapper;
class AVCodecWrapper;
class AVPacketWrapper;
class AVChannelLayoutWrapper;

struct AVCodecIDResolver final
{
    AVCodecIDFwd (* GetAVCodecID)(AudacityAVCodecID);
    AudacityAVCodecID (* GetAudacityCodecID)(AVCodecIDFwd);
};

struct AVCodecFactories final
{
    std::unique_ptr<AVCodecContextWrapper> (* CreateAVCodecContextWrapper)(const FFmpegFunctions&, AVCodecContext*) = nullptr;
    std::unique_ptr<AVCodecContextWrapper> (* CreateAVCodecContextWrapperFromCodec)(const FFmpegFunctions&,
                                                                                    std::unique_ptr<AVCodecWrapper>) = nullptr;
    std::unique_ptr<AVCodecWrapper> (* CreateAVCodecWrapper)(const AVCodec*) = nullptr;

    //! @post return value is not null
    std::unique_ptr<AVPacketWrapper> (* CreateAVPacketWrapper)(const FFmpegFunctions&) = nullptr;
};

class AVFormatContextWrapper;
class AVInputFormatWrapper;
class AVIOContextWrapper;
class AVOutputFormatWrapper;
class AVStreamWrapper;

struct AVFormatFactories final
{
    std::unique_ptr<AVFormatContextWrapper> (* CreateAVFormatContextWrapper)(const FFmpegFunctions&) = nullptr;
    std::unique_ptr<AVInputFormatWrapper> (* CreateAVInputFormatWrapper)(AVInputFormat*) = nullptr;
    std::unique_ptr<AVIOContextWrapper> (* CreateAVIOContextWrapper)(const FFmpegFunctions&) = nullptr;
    std::unique_ptr<AVOutputFormatWrapper> (* CreateAVOutputFormatWrapper)(const AVOutputFormat*) = nullptr;
    std::unique_ptr<AVStreamWrapper> (* CreateAVStreamWrapper)(const FFmpegFunctions&, AVStream*, bool) = nullptr;
};

class AVFrameWrapper;

struct AVUtilFactories final
{
    //! @post return value is not null
    std::unique_ptr<AVFrameWrapper> (* CreateAVFrameWrapper)(const FFmpegFunctions&) = nullptr;
    std::unique_ptr<FFmpegLog> (* CreateLogCallbackSetter)(const FFmpegFunctions&) = nullptr;

    std::unique_ptr<AVChannelLayoutWrapper> (* CreateDefaultChannelLayout)(const FFmpegFunctions&, int channelsCount) = nullptr;
    std::unique_ptr<AVChannelLayoutWrapper> (* CreateLegacyChannelLayout)(const FFmpegFunctions&, uint64_t layout,
                                                                          int channelsCount) = nullptr;
    std::unique_ptr<AVChannelLayoutWrapper> (* CreateAVChannelLayout)(const FFmpegFunctions&, const AVChannelLayout* layout) = nullptr;
};

class FFmpegAPIResolver final
{
    FFmpegAPIResolver() = default;

public:
    static void Register();
    static FFmpegAPIResolver& Get();

    bool GetAVCodecIDResolver(int avCodecVersion, AVCodecIDResolver& resolver) const;
    bool GetAVCodecFactories(int avCodecVersion, AVCodecFactories& factories) const;
    bool GetAVFormatFactories(int avFormatVersion, AVFormatFactories& factories) const;
    bool GetAVUtilFactories(int avUtilVersion, AVUtilFactories& factories) const;

    void AddAVCodecIDResolver(int avCodecVersion, const AVCodecIDResolver& resolver);
    void AddAVCodecFactories(int avCodecVersion, const AVCodecFactories& factories);
    void AddAVFormatFactories(int avFormatVersion, const AVFormatFactories& factories);
    void AddAVUtilFactories(int avUtilVersion, const AVUtilFactories& factories);

    //! Compatible library versions to be sought at load time, ordered by
    //! decreasing preference (that is, newest version first)
    std::vector<int> GetSuportedAVFormatVersions() const;

private:
    std::map<int, AVCodecIDResolver> mAVCodecIDResolvers;
    std::map<int, AVCodecFactories> mAVCodecFactories;
    std::map<int, AVFormatFactories> mAVFormatFactories;
    std::map<int, AVUtilFactories> mAVUtilFactories;
};
